
#include "../iface/APRInterface.h"
#include <apr-1/apr_strings.h>
#include "BaseConvert.h"
#include "StringUtils.h"
#include "StreamToDirectory.h"

#define min_v(a, b) (((a) < (b)) ? (a) : (b))

StreamToDirectory::StreamToDirectory(const char *directorypath, apr_pool_t *imp)
{
   apr_status_t status;
   status = apr_pool_create(&mp, imp);
   CheckAPRError(status);
   directory_name = (char *)strdup(directorypath);
   nxtfilename = NULL;
   currflag = 0;
   currbytesthrough = 0;
}

StreamToDirectory::~StreamToDirectory()
{
   if (nxtfilename) free(nxtfilename);
   nxtfilename = NULL;
   if (directory_name) free(directory_name);
   directory_name = NULL;
}

int64_t StreamToDirectory::readLogical64Bit(unsigned char *bytes)
{
   int64_t rd = 0;
   for (int i = 8; i--; )
   {
      rd += bytes[i];
      if (i)
         rd *= 256;
   }
   return rd;
}

apr_off_t StreamToDirectory::putNextBlock(char *blockbuffer, apr_off_t tsize)
{
   apr_pool_clear(mp);
   apr_status_t status;
   int count = 0;
   while (count < tsize)
   {
      apr_off_t tlen = tsize - count;
      int retval = recreateFile(&(blockbuffer[count]), &tlen);
      if (retval >= 0) count += tlen;
   }
   return count;
}

int StreamToDirectory::recreateFile(char *blockbuffer, apr_off_t *maxlen)
{
   apr_status_t status;
   apr_off_t remlen = *maxlen;
   apr_off_t pnt = 0;
   if (currflag == READING_FILE_NAME_SIZE)
   {
      apr_off_t by = min_v(8-currbytesthrough, remlen);
      memcpy(&(nxtfilenamesze_encoded[currbytesthrough]),
             &(blockbuffer[pnt]),
             by);
      pnt += by;
      remlen -= by;
      if (!remlen)
      {
         currbytesthrough += by;
         *maxlen = pnt;
         return 1;
      } else
      {
         nxtfilenamesze = readLogical64Bit(nxtfilenamesze_encoded);
         if (nxtfilename) free(nxtfilename);
         nxtfilename = (char *)malloc(sizeof(char)*(nxtfilenamesze+1));
         currbytesthrough = 0;
         currflag = READING_FILE_STREAM_SIZE;
      }
   }
   if (currflag == READING_FILE_STREAM_SIZE)
   {
      apr_off_t by = min_v(8-currbytesthrough, remlen);
      memcpy(&(nxtfilestreamsze_encoded[currbytesthrough]),
             &(blockbuffer[pnt]),
             by);
      pnt += by;
      remlen -= by;
      if (!remlen)
      {
         currbytesthrough += by;
         *maxlen = pnt;
         return 1;
      } else
      {
         nxtfilestreamsze = readLogical64Bit(nxtfilestreamsze_encoded);
         currbytesthrough = 0;
         currflag = READING_NAME;
      }
   }
   if (currflag == READING_NAME)
   {
      apr_off_t by = min_v(nxtfilenamesze-currbytesthrough, remlen);
      memcpy(&(nxtfilename[currbytesthrough]),
             &(blockbuffer[pnt]),
             by);
      pnt += by;
      remlen -= by;
      if (!remlen)
      {
         currbytesthrough += by;
         *maxlen = pnt;
         return 1;
      } else
      {
         nxtfilename[nxtfilenamesze] = '\0';
         currbytesthrough = 0;
         currflag = READING_STREAM;
      }
   }
   if (currflag == READING_STREAM)
   {
      apr_off_t by = min_v(nxtfilestreamsze-currbytesthrough, remlen);
      apr_file_t *newFP;
      apr_status_t status;
      char *tfilename = StringUtils::varprintf("%s/%s", directory_name, nxtfilename);
      char *qfilename = strdup(tfilename);
      int last = -1;
      for (int i = 0; i < strlen(qfilename); i++)
         last = (qfilename[i] == '/') ? i : last;
      qfilename[last] = '\0';
      status = apr_dir_make_recursive(qfilename,
         APR_FPROT_UREAD|APR_FPROT_UWRITE|APR_FPROT_UEXECUTE, mp);
      CheckAPRError(status);
      printf("filedir to create: %s\n", qfilename);
      free(qfilename);
      status = apr_file_open(&newFP, tfilename,
         APR_FOPEN_CREATE|APR_FOPEN_BINARY|APR_FOPEN_WRITE|APR_FOPEN_BUFFERED,
         APR_FPROT_UREAD|APR_FPROT_UWRITE|APR_FPROT_UEXECUTE, mp);
      printf("filename to create: %s\n", tfilename);
      CheckAPRError(status);
      apr_off_t cbt = currbytesthrough;
      status = apr_file_seek(newFP, APR_SET, &cbt);
      CheckAPRError(status);
      apr_size_t byteswritten = 0;
      status = apr_file_write_full(newFP, &(blockbuffer[pnt]), by, &byteswritten);
      CheckAPRError(status);
      status = apr_file_close(newFP);
      CheckAPRError(status);
      free(tfilename);
      by = byteswritten;
      pnt += by;
      remlen -= by;
      if (!remlen)
      {
         currbytesthrough += by;
         *maxlen = pnt;
         return 1;
      } else
      {
         currbytesthrough = 0;
         currflag = READING_FILE_NAME_SIZE;
         *maxlen = pnt;
         return 0;
      }
   }
   *maxlen = pnt;
   return -1;
}
